// ==================================================================================
// Shared Genomics Project MPI Codebase
// Version 1.0 30/04/2010
//
// (c) 2010 University of Manchester all rights reserved
//
// This file is distributed under the GNU General Public License, Version 2.  
// Please see the file COPYING.txt for more details
// ==================================================================================

/*!
\file
\brief	Implementation of I/O Methods for Parrallised Filtering
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "bfilter.h"
#include "BitString.h"
#include "bmapfile.h"
#include "copyfile.h"
#include "individual.h"
#include "missingnessfile.h"
#include "pedfile.h"
#include "pformat.h"
#include "sample.h"
#include "spedfile.h"
#include "suffixes.h"
#include "utils.h"

// If a sample index specified in the category filter, then value[person_index] is set as 1.
int* _pformat_parse_exclude_list(struct selected_options *ops, int nSamples) {
	int *values = NULL;
	char line[80];

	if (ops == NULL || nSamples <= 0) return NULL;
	else if (strlen(ops->szExcludeListFileName) == 0) return NULL;
	values = create_int_array(nSamples, 0);
	if (values != NULL) {
		FILE *in = fopen(ops->szExcludeListFileName, "r");
		if (in != NULL) {
			while (fgets(line, 80, in)) {
				int person_index = atoi(line);
				if (person_index >= 0 && person_index <= nSamples) values[person_index] = 1;
			}
			fclose(in);
		}	
	}

	return values;
}

int pformat_write_filter_log(int nSNPs, struct small_locus *all_snps, BOOL *snps_to_del, char *reasons_to_del, double *stats, struct selected_options *ops, char *ldir, char *rdir, int nSamples, BOOL *persons_to_del, double *missingness) {
	char localFilepath[FILE_PATH_LENGTH], remoteFilepath[FILE_PATH_LENGTH];
	BOOL jobDone = FALSE;
	FILE *out = NULL;
	int *category_filter = NULL;

	if (nSNPs <= 0 || snps_to_del == NULL  || reasons_to_del == NULL || stats == NULL || ops == NULL || ldir == NULL || rdir == NULL || nSamples <= 0 || persons_to_del == NULL || all_snps == NULL || missingness == NULL) 
		return 0;
	else if (strlen(ops->szRunId) == 0 || strlen(ldir) == 0 || strlen(rdir) == 0) return 0;

	// Need to re-load the category data as the indices aren't stored.
	category_filter = _pformat_parse_exclude_list(ops, nSamples);
	
	sprintf(localFilepath, "%s%s%s", ldir, ops->szRunId, SUFFIX_FILTER_LOG);
	sprintf(remoteFilepath, "%s%s%s", rdir, ops->szRunId, SUFFIX_FILTER_LOG);
	out = fopen(localFilepath, "w");
	if (out != NULL) {
		int i;
		for (i = 0; i < nSamples; i++) {
			if (persons_to_del[i]) {
				// Just assume missingness.
				if (category_filter  == NULL) fprintf(out, "%i %i %2.6f\n", i, FILTER_REASON_PERSON_MISSINGNESS, missingness[i]);
				else if (category_filter[i] != 1) fprintf(out, "%i %i %2.6f\n", i, FILTER_REASON_PERSON_MISSINGNESS, missingness[i]);
			}
		}
		for (i = 0; i < nSNPs; i++) {
			if (snps_to_del[i]) fprintf(out, "%s %i %1.16f\n", all_snps[i].name, reasons_to_del[i], stats[i]);
		}
		fclose(out);
		jobDone = copyAndDeleteFile(localFilepath, remoteFilepath) ? TRUE : FALSE;
	}
	if (category_filter != NULL) free(category_filter);
	
	if (jobDone) return 1;
	else return 0;
}

// Write out the dimensions of the filtered data-set.
int pformat_write_filter_dims(int nSNPs, int nSamples, int numprocs, BOOL *snps_to_del, BOOL *persons_to_del, struct selected_options *ops) {
	char localFilepath[FILE_PATH_LENGTH], remoteFilepath[FILE_PATH_LENGTH];
	FILE *out = NULL;
	BOOL jobDone = FALSE;
	
	if (numprocs <= 0 || nSNPs <= 0 || nSamples <= 0 || snps_to_del == NULL  || persons_to_del == NULL || ops == NULL) return 0;
	else if (strlen(ops->szRunId) == 0 || strlen(ops->ldir) == 0 || strlen(ops->rdir) == 0) return 0;

	sprintf(localFilepath, "%s%s%s", ops->ldir, ops->szRunId, SUFFIX_DATASET_DIMS);
	sprintf(remoteFilepath, "%s%s%s", ops->rdir, ops->szRunId, SUFFIX_DATASET_DIMS);

	out = fopen(localFilepath, "w");
	if (out != NULL) {
		int i, snp_count = 0, person_count = 0;
		for (i = 0; i < nSNPs; i++) if (!snps_to_del[i]) snp_count++;
		for (i = 0; i < nSamples; i++) if (!persons_to_del[i]) person_count++;

		if (fprintf(out, "%i %i %i\n", numprocs, snp_count, person_count)) jobDone = TRUE;
		fclose(out);
		if (jobDone) jobDone = copyAndDeleteFile(localFilepath, remoteFilepath) ? TRUE : FALSE;
	}

	if (jobDone) return 1;
	else return 0;
}

int pformat_read_filter_dims(int *numprocs, int *nSNPs, int *nSamples, struct selected_options *ops) {
	FILE *in = NULL;
	BOOL jobDone = FALSE;
	char line[256], token[80], filepath[FILE_PATH_LENGTH];

	if (nSNPs == NULL || nSamples == NULL || numprocs == NULL || ops == NULL) return 0;
	else if (strlen(ops->szRunId) == 0 || strlen(ops->ldir) == 0) return 0;

	*nSNPs = *nSamples = *numprocs = 0;
	sprintf(filepath, "%s%s%s",  ops->rdir, ops->szRunId, SUFFIX_DATASET_DIMS);
	in = fopen(filepath, "r");
	if (in != NULL) {
		if (fgets(line, 256, in) != NULL) {
			get_token(line, token, 0, ' ');
			*numprocs = atoi(token);
			get_token(line, token, 1, ' ');
			*nSNPs = atoi(token);
			get_token(line, token, 2, ' ');
			*nSamples = atoi(token);

			jobDone = TRUE;

			if (*numprocs <= 0 || *nSNPs <= 0 || *nSamples <= 0) jobDone = FALSE;
		}
		fclose(in);
	}

	if (jobDone) return 1;
	else return 0;
}

int pformat_read_fbped_fragments(struct selected_options *ops, struct mapfile *m) {
	BOOL jobDone = FALSE;
	char localFilepath[FILE_PATH_LENGTH], remoteFilepath[FILE_PATH_LENGTH], *buffer = NULL;
	int filtered_snp_count = 0, filtered_person_count = 0, expected_numprocs = 0, snp_count = 0, i, j;
	size_t nRecord_size;

	if (ops == NULL || m == NULL) return 0;
	else if (m->nSNP != 0 || m->SNP != NULL) return 0;
	else if (strlen(ops->ldir) == 0 || strlen(ops->rdir) == 0) return 0;

	if(!pformat_read_filter_dims(&expected_numprocs, &filtered_snp_count, &filtered_person_count, ops)) return 0;
	if(!mapfile_alloc_SNP(filtered_snp_count, filtered_person_count, m)) return 0;

	// Read each BPED fragment in turn from the local working directory.
	nRecord_size = sizeof(char) * filtered_person_count * 2;
	buffer = (char*) malloc(nRecord_size);
	if (buffer != NULL) {
		for (i = 0; i < expected_numprocs; i++) {
			sprintf(remoteFilepath, "%s%s_%i%s", ops->rdir, ops->szRunId, i, SUFFIX_FBPED_FILE);
			sprintf(localFilepath, "%s%s_%i%s", ops->ldir, ops->szRunId, i, SUFFIX_FBPED_FILE);
			if (copyFile(remoteFilepath, localFilepath)) {
				FILE *in = fopen(localFilepath, "rb");
				if (in != NULL) {
					size_t nBytes = 0;
					if(fread(&nBytes, sizeof(size_t), 1, in) == 1) {
						while(fread(buffer, nRecord_size, 1, in)) {
							char *pBuffer = buffer;
							BOOL *pOne = m->SNP[snp_count]->one;
							BOOL *pTwo = m->SNP[snp_count]->two;

							for (j = 0; j < filtered_person_count; j++, pBuffer += sizeof(char) * 2) {
								pOne[j] = pBuffer[0]; // Minor allele
								pTwo[j] = pBuffer[1]; // Major allele
							}
							snp_count++;
						}
					}
					fclose(in);
					deleteFile(localFilepath);
				} else 
					break;
			}
		}
	}
	if (buffer != NULL) free(buffer);

	// Check that all the expected data has been read in.
	if (snp_count == filtered_snp_count) jobDone = TRUE;
	
	if (jobDone) return 1;
	else return 0;
}

int pformat_alloc_samples(struct pedfile *p, int nSamples, double *phe_values, BOOL *persons_to_del, BOOL *missing, BOOL *sex) {
	BOOL jobDone = FALSE;
	int i, j, nPeople_to_write = 0, nPeople_wrote = 0;

	if (p == NULL || nSamples <= 0 || phe_values == NULL || persons_to_del == NULL || missing == NULL || sex == NULL) return 0;
	else if (p->sample != NULL || p->nSample != 0) return 0;

	for (i = 0; i < nSamples; i++) if(!persons_to_del[i])  nPeople_to_write++;
	if (nPeople_to_write == 0) return 0;
	if (!pedfile_alloc_sample(p, nPeople_to_write)) return 0;
	
	for (i = 0, j = 0; i < nSamples; i++) {
		if (persons_to_del[i]) continue; 
		else {
			struct individual *person = individual_create();
			if (person != NULL) {
				person->sex = sex[i];
				person->phenotype = phe_values[i];
				person->missing = missing[i];
				p->sample[j++] = person;
				nPeople_wrote++;
			}

			if (nPeople_wrote == nPeople_to_write) {
				jobDone = TRUE;
			}
		}
	}

	if (jobDone) return 1;
	else return 0;
}


int pformat_cleanup(struct selected_options *ops) {
	char filepath[FILE_PATH_LENGTH];
	int numprocs = 0, nSNPs = 0, nSamples = 0, i;

	if (ops == NULL) return 0;
	else if (strlen(ops->szRunId) == 0 || strlen(ops->ldir) == 0) return 0;

	// Delete the FBPED fragments
	if(pformat_read_filter_dims(&numprocs, &nSNPs, &nSamples, ops)) {
		for (i = 0; i < numprocs; i++) {
			sprintf(filepath, "%s%s_%i%s", ops->rdir, ops->szRunId, i, SUFFIX_FBPED_FILE);
			deleteFile(filepath);
		}
		sprintf(filepath, "%s%s%s", ops->rdir, ops->szRunId, SUFFIX_DATASET_DIMS);
		deleteFile(filepath);
		sprintf(filepath, "%s%s%s", ops->rdir, ops->szRunId, SUFFIX_SNP_TO_DEL);
		deleteFile(filepath);
		sprintf(filepath, "%s%s%s", ops->rdir, ops->szRunId, SUFFIX_FILTER_LOG);
		deleteFile(filepath);
	}
	
	return 1;
}